[アップデート] Amazon CloudFront と Lambda 関数 URL オリジンの OAC の設定が AWS CDK で簡単に実装可能になりました
こんにちは、製造ビジネステクノロジー部の若槻です。
AWS CDK の最近のリリースである v2.168.0 で、下記の機能アップデートが追加されていました。
cloudfront: function URL origin access control L2 construct (#31339) (b8f47c8), closes #31629
今年 2024 年 4 月に、Amazon CloudFront が Lambda 関数 URL オリジンに対して Origin Access Control (OAC) をサポートするようになりました。これにより指定の CloudFront Distribution からのみ のアクセスを許可し、Lambda 関数 URL オリジンを保護することが可能となりました。
今回の AWS CDK のアップデートにより、この CloudFront と Lambda 関数 URL の OAC を CDK の L2 Construct で簡単に設定できるようになりました。
試してみた
CDK パッケージのアップデート
AWS CDK モジュールを v2.168.0 以上にアップデートします。
npm i aws-cdk-lib@latest aws-cdk@latest
CDK 実装
AWS CDK で CloudFront と Lambda 関数 URL の OAC を設定する実装です。FunctionUrlOrigin
クラスの withOriginAccessControl
メソッドを使用して CloudFront Distribution のオリジンに Lambda 関数 URL を指定することにより、OAC での保護も合わせて設定できます。
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as lambda_nodejs from 'aws-cdk-lib/aws-lambda-nodejs';
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';
export class MainStack extends cdk.Stack {
constructor(scope: Construct, id: string) {
super(scope, id);
const myFunction = new lambda_nodejs.NodejsFunction(this, 'MyFunction');
const myFunctionUrl = myFunction.addFunctionUrl({
authType: lambda.FunctionUrlAuthType.AWS_IAM, // 認証タイプとして AWS_IAM を指定し、OAC で保護可能とする
});
new cloudfront.Distribution(this, 'MyDistribution', {
defaultBehavior: {
origin:
// @see https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_cloudfront.FunctionUrlOriginAccessControl.html
origins.FunctionUrlOrigin.withOriginAccessControl(myFunctionUrl),
},
});
}
}
参考までに、OAC で保護せずに Lambda 関数 URL を指定する場合のコードも折りたたみで記載しておきます。
OAC で保護しない場合
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as lambda_nodejs from 'aws-cdk-lib/aws-lambda-nodejs';
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';
export class MainStack extends cdk.Stack {
constructor(scope: Construct, id: string) {
super(scope, id);
const myFunction = new lambda_nodejs.NodejsFunction(this, 'MyFunction');
const myFunctionUrl = myFunction.addFunctionUrl({
authType: lambda.FunctionUrlAuthType.AWS_IAM,
});
new cloudfront.Distribution(this, 'MyDistribution', {
defaultBehavior: {
origin: new origins.FunctionUrlOrigin(myFunctionUrl), // OAC で保護しない場合
},
});
}
}
Lambda 関数のコードは以下のようになります。
export const handler = async (event: any): Promise<any> => {
// リクエスト情報を使用してレスポンスを返す
const response = {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: 'Hello from Lambda!',
request: event,
}),
};
return response;
};
上記の実装を CDK によりデプロイすると、オリジンに Lambda 関数 URL が指定され、オリジンの保護に OAC が使用されていることが確認できました。
動作確認
作成された CloudFront Distribution の URL にアクセスすると、Lambda 関数 URL からレスポンスを取得できました。OAC によるオリジンのアクセスでは 856369053181
という AWS マネージドアカウントが使用されているようです。
curl https://d15lafwe30yr9y.cloudfront.net/ | jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 2434 100 2434 0 0 45144 0 --:--:-- --:--:-- --:--:-- 45074
{
"message": "Hello from Lambda!",
"request": {
"version": "2.0",
"routeKey": "$default",
"rawPath": "/",
"rawQueryString": "",
"headers": {
"x-amz-content-sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"x-amzn-tls-version": "TLSv1.2",
"x-amz-date": "20241125T120801Z",
"x-forwarded-proto": "https",
"x-amz-source-account": "XXXXXXXXXXXX",
"x-forwarded-port": "443",
"x-forwarded-for": "XXX.XXX.XXX.XXX",
"x-amz-security-token": "IQoJb3JpZ...",
"via": "2.0 6a4098eaf995c1e965d6434534971664.cloudfront.net (CloudFront)",
"x-amz-source-arn": "arn:aws:cloudfront::XXXXXXXXXXXX:distribution/E19HGLBL0VS740",
"x-amzn-tls-cipher-suite": "ECDHE-RSA-AES128-GCM-SHA256",
"x-amzn-trace-id": "Root=1-674468a1-2b9a07f44a81e2e8430ef07d",
"host": "znlxyvy3yhvmdbxhjt4forfmca0cylol.lambda-url.ap-northeast-1.on.aws",
"x-amz-cf-id": "rAQGmEaAmDX3GH9Y-kPFsG2Vat_rMKIH0EQzKLmps4EhMJX6nmsVbg==",
"user-agent": "Amazon CloudFront"
},
"requestContext": {
"accountId": "856369053181",
"apiId": "znlxyvy3yhvmdbxhjt4forfmca0cylol",
"authorizer": {
"iam": {
"accessKey": "ASIXXXXXXXXXXXXXXXXX",
"accountId": "856369053181",
"callerId": "AROA4OY4SXX6VEIZRJRCJ:OriginAccessSession",
"cognitoIdentity": null,
"principalOrgId": null,
"userArn": "arn:aws:sts::856369053181:assumed-role/OriginAccessControlRole/OriginAccessSession",
"userId": "AROA4OY4SXX6VEIZRJRCJ:OriginAccessSession"
}
},
"domainName": "znlxyvy3yhvmdbxhjt4forfmca0cylol.lambda-url.ap-northeast-1.on.aws",
"domainPrefix": "znlxyvy3yhvmdbxhjt4forfmca0cylol",
"http": {
"method": "GET",
"path": "/",
"protocol": "HTTP/1.1",
"sourceIp": "64.252.113.135",
"userAgent": "Amazon CloudFront"
},
"requestId": "d280dbea-fe7e-40e8-a43a-fd9fbf021b78",
"routeKey": "$default",
"stage": "$default",
"time": "25/Nov/2024:12:08:01 +0000",
"timeEpoch": 1732536481736
},
"isBase64Encoded": false
}
}
Lambda 関数 URL に直接アクセスすると、ブラウザの場合は 403 Forbidden
が返されました。
curl でアクセスすると、こちらはレスポンスが返ってきませんでした。OAC による保護もちゃんと働いているようです。
curl znlxyvy3yhvmdbxhjt4forfmca0cylol.lambda-url.ap-northeast-1.on.aws
参考
注意点
Lambda 関数 URL の返却内容を更新して CDK デプロイをしても、CloudFront Distribution からアクセス時のレスポンスが更新されませんでした。その場合は、CloudFront Distribution のキャッシュが原因である可能性があります。
よって Distribution のパスに対してバリデーションを行うことにより、更新後の Lambda 関数のレスポンスを取得可能となりました。
aws_s3_deployment Construct クラスのように CDK デプロイ時にデフォルトでバリデーションが行われる挙動とならない点には注意しましょう。
おわりに
Amazon CloudFront と Lambda 関数 URL オリジンの OAC の設定が AWS CDK で簡単に実装可能になったので、共有しました。
生成 AI による処理を行う API を公開する場合に CloudFront と Lambda 関数 URL の組み合わせを採用するケースがチラホラ見られます。そのような構成で OAC による保護が CDK で簡単に設定できるのはなかなか嬉しいのではないでしょうか。
以上